import axios, { AxiosError, AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'

import { config } from './config'
import { useUserStoreHook } from '@/store/modules/user'
import { useRequestStoreHook } from '@/store/modules/request'
import CacheKey from '@/constants/cache-key'
import { getToken } from '@/utils/cookies'

/** 退出登录并强制刷新页面 */
async function logout() {
  await useUserStoreHook().resetToken()
  location.reload()
}

const { result_code, request_timeout } = config

// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时，必须是以下类型之一：
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属：FormData, File, Blob
// - Node 专属： Stream
// 解决参考 https://blog.csdn.net/HermitSun/article/details/89889743

// 'responseType' 表示服务器响应的数据类型
// 选项有:'arraybuffer'， 'document'， 'json'， 'text'， 'stream'
// 只支持浏览器: 'blob'

const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_BASE_API,
  timeout: request_timeout
})

/** 请求拦截 */
service.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    if (config) {
      // 未设置 signal 的请求，设置取消请求方法。已设置 signal 的请求需要自行取消
      if (!config.signal) {
        // 从 v0.22.0 开始，Axios 支持以 fetch API 方式，不兼容 IE
        // CancelToken 已被弃用，不应在新项目中使用
        const controller = new AbortController()
        config.signal = controller.signal
        useRequestStoreHook().add(controller)
      }
      // 请求携带 token，优先使用传入的 token
      if (!config.headers[CacheKey.AUTHORIZATION_KEY]) {
        config.headers[CacheKey.AUTHORIZATION_KEY] = `Bearer ${getToken()}`
      }
      // 上传文件取消超时
      if (config.headers['Content-Type'] === 'multipart/form-data') {
        config.timeout = 0
      }
      // 下载文件取消超时
      if (config.responseType && ['arraybuffer', 'blob'].includes(config.responseType)) {
        config.timeout = 0
      }
    }
    return config
  },
  // 发送失败
  (error: AxiosError) => {
    return Promise.reject(error)
  }
)

/** 响应拦截 */
service.interceptors.response.use(
  async (response: AxiosResponse) => {
    // responseData 是 API 返回的数据
    const responseData = response.data
    // 二进制数据则直接返回
    if (['blob', 'arraybuffer'].includes(response.request?.responseType)) return responseData

    const { msg, code, data } = responseData

    // 根据 code 值判断是否成功
    switch (code) {
      case result_code:
        return responseData
      case '-1000':
        ElMessage({
          message: data,
          type: 'error'
        })
        // 检测到token过期，直接删除token
        await logout()
        return Promise.reject(new Error(msg))
      case '30003':
        ElMessage({
          message: data,
          type: 'error'
        })
        return Promise.reject(new Error(msg))
      default:
        ElMessage({
          message: msg,
          type: 'error'
        })
        return Promise.reject(new Error(msg))
    }
  },
  async (error: AxiosError) => {
    console.log('AxiosError: ', error)
    // Status 是 HTTP 状态码
    const { status } = error.response as AxiosResponse
    const useStore = useUserStoreHook()

    switch (status) {
      case 400:
        error.message = '请求错误'
        break
      case 401:
        // Token 过期时，直接退出登录并强制刷新页面（会重定向到登录页）
        await useStore.logout()
        break
      case 403:
        error.message = '拒绝访问'
        break
      case 404:
        error.message = '请求地址出错'
        break
      case 408:
        error.message = '请求超时'
        break
      case 500:
        error.message = '服务器内部错误'
        break
      case 501:
        error.message = '服务未实现'
        break
      case 502:
        error.message = '网关错误'
        break
      case 503:
        error.message = '服务不可用'
        break
      case 504:
        error.message = '网关超时'
        break
      case 505:
        error.message = 'HTTP 版本不受支持'
        break
      default:
        break
    }
    ElMessage({ message: error.message, type: 'error' })
    return Promise.reject(error)
  }
)

export default service
